#include "operator.h"

#include <iostream>
#include <math.h>

#include "cache.h"
extern Cache CACHE;

#define PI 3.14159265

uint32 Operator::get( string f_string, int32 f_class )
{
	if ( f_class == 0 || f_class == -1 )
	{
		if ( f_string == "!" )
			return FAT;
		else if ( f_string == "l" )
			return LOG;
		else if ( f_string == "c" )
			return COS;
		else if ( f_string == "s" )
			return SIN;
		else if ( f_string == "t" )
			return TAN;
		else if ( f_string == "m" )
			return MOD;
		else if ( f_string == "e" )
			return EXPTEN;
	}
	if ( f_class == 1 || f_class == -1 )
	{
		if ( f_string == "^" )
			return EXP;
		else if ( f_string == "r" )
			return SQRT;
	}

	if ( f_class == 2 || f_class == -1 )
	{
		if ( f_string == "*" )
			return MULT;
		else if ( f_string == "/" )
			return DIV;
	}

	if ( f_class == 3 || f_class == -1 )
	{
		if ( f_string == "+" )
			return PLUS;
		else if ( f_string == "-" )
			return MINUS;
	}

	return 0;
}

Value Operator::fatorial( Value f_value )
{
	Value f_tmp = 1;

	if ( f_value.get() < 0 )
	{
		f_value.set( f_value.get() * -1 );
		f_tmp = -1;
	}

	for ( uint32 f_all = 2; f_all <= f_value.get(); ++f_all )
		f_tmp.set( f_tmp.get() * f_all );

	return f_tmp;
}

string Operator::execute( uint32 f_op, Value f_v1, Value f_v2 )
{
	switch ( f_op )
	{
		case PLUS:   return CACHE.add_get( f_v1.get() + f_v2.get() );
		case MINUS:  return CACHE.add_get( f_v1.get() - f_v2.get() );
		case MULT:   return CACHE.add_get( f_v1.get() * f_v2.get() );
		case DIV:    return CACHE.add_get( f_v1.get() / f_v2.get() );
		case EXP:    return CACHE.add_get( pow( f_v1.get(), f_v2.get() ) );
		case SQRT:   return CACHE.add_get( pow( f_v2.get(), 1 / ( f_v1.get() == 0 ? 2 : f_v1.get() ) ) );
		case FAT:    return CACHE.add_get( fatorial( f_v1 ).get() / ( f_v2.get() == 0 ? 1 : fatorial( f_v2 ).get() ) );
		case LOG:    return CACHE.add_get( log10( f_v1.get() ) / ( f_v2.get() == 0 ? 1 : log10( f_v2.get() ) ) );
		case COS:    return CACHE.add_get( ( f_v1.get() == 0 ? 1 : f_v1.get() ) * cos( f_v2.get() * PI/180 ) );
		case SIN:    return CACHE.add_get( ( f_v1.get() == 0 ? 1 : f_v1.get() ) * sin( f_v2.get() * PI/180 ) );
		case TAN:    return CACHE.add_get( ( f_v1.get() == 0 ? 1 : f_v1.get() ) * tan( f_v2.get() * PI/180 ) );
		case MOD:    return CACHE.add_get( fmod( f_v1.get(), f_v2.get() ) );
		case EXPTEN: return CACHE.add_get( ( f_v1.get() == 0 ? 1 : f_v1.get() ) * pow( 10, f_v2.get() == 0 ? 1 : f_v2.get() ) );
	}
	return "";
}

string Operator::execute( string f_string )
{
	std::cout << "Operating I" << CACHE.size() << ": " << f_string;

	uint32 f_op;
	Value f_v1, f_v2;
	for ( uint32 f_all = 0; f_all < f_string.size(); ++f_all )
	{
		f_op = Operator::get( f_string.substr( f_all, 1 ) );
		if ( f_all == 0 && f_op == MINUS )
		{
			if ( f_all < f_string.size() - 1 )
				++f_all;
			else break;

			f_op = Operator::get( f_string.substr( f_all, 1 ) );
		}

		if ( f_op != 0 )
		{
			if ( f_all > 0 )
			{
				if ( f_string.substr( 0, f_all ).find( "I" ) != -1 )
				{
					if ( f_string.substr( 0, 1 ) == "-" )
						f_v1 = -1 * CACHE.get( f_string.substr( 1, f_all - 1 ) ).get();
					else f_v1 = CACHE.get( f_string.substr( 0, f_all ) );
				}
				else f_v1 = f_string.substr( 0, f_all );
			}
			else f_v1 = 0;

			if ( f_all < f_string.size() - 1 )
			{
				if ( f_string.substr( f_all + 1 ).find( "I" ) != -1 )
				{
					if ( f_string.substr( f_all, 1 ) == "-" && get( f_string.substr( f_all - 1, 1 ) ) != 0 )
						f_v2 = -1 * CACHE.get( f_string.substr( f_all + 2 ) ).get();
					else f_v2 = CACHE.get( f_string.substr( f_all + 1 ) );
				}
				else f_v2 = f_string.substr( f_all + 1 );
			}
			else f_v2 = 0;
			break;
		}
	}

	string f_tmp = execute( f_op, f_v1, f_v2 );
	std::cout << " = " << CACHE.get( f_tmp ).get() << ", ";

	return f_tmp;
}
